/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved.
 *
 */

import path from 'path';

import { hvigor, PathUtil } from '@ohos/hvigor';
import { ClassTypeConst } from '@ohos/hvigor/src/base/common/options/class-type-const.js';
import { nodeConfigPathInfo } from '@ohos/hvigor/src/common/node-config-path-info';
import fse from 'fs-extra';

import { CommonConst } from '../../../const/common-const';
import { getJson5Obj } from '../../json-util';
import { OhosLogger } from '../../log/ohos-logger';
import { configFileLoader } from '../config-file-loader';


const _log: OhosLogger = OhosLogger.getLogger('OhPackageLoader');

class OhPackageLoader {
  // 标志位，如果发生了修改，在后续的hook生命周期中实现修改后依赖文件落盘
  public isUpdatedOhPackageInfo = false;

  // 标志位，判断依赖信息是否加载到loader中，函数不放在constructor是因为会被clean
  private isLoaded = false;

  // 标志位，根据是否修改了parameterFile文件来判断是否要执行ohpm操作（此变量不考虑自定义任务修改ohpackage依赖）
  public isChangedParameterFile = false;

  // key:origin path value:update path
  public OhPackagePathMap = new Map<string, string>();

  // key:update path value: old path 有些代码根据原有工程结构中的ohpackage路径硬编码，此处存储map供快速找到原路径
  public UpdatePathToOriginMap = new Map<string, string>();

  public getDependenciesOpt(nodeOhPackagePath: string): any {
    this.saveOhPackagePathInfoToLoader();
    return this.getOhPackageJsonObj(nodeOhPackagePath)?.dependencies;
  }

  public clean() {
    this.isUpdatedOhPackageInfo = false;
    this.isLoaded = false;
    this.OhPackagePathMap = new Map<string, string>();
    this.UpdatePathToOriginMap = new Map<string, string>();
    this.isChangedParameterFile = false;
  }

  public getDevDependenciesOpt(nodeOhPackagePath: string): any {
    this.saveOhPackagePathInfoToLoader();
    return this.getOhPackageJsonObj(nodeOhPackagePath)?.devDependencies;
  }

  public getDynamicDependenciesOpt(nodeOhPackagePath: string): any {
    this.saveOhPackagePathInfoToLoader();
    return this.getOhPackageJsonObj(nodeOhPackagePath)?.dynamicDependencies;
  }

  public getOhPackageJsonObj(nodeOhPackagePath: string): Record<string, any> | undefined {
    return configFileLoader.getConfigFileJson(this.UpdatePathToOriginMap.get(nodeOhPackagePath) ?? nodeOhPackagePath);
  }

  public setDependenciesOpt(nodeOhPackagePath: string, dependencies: any) {
    this.validateDependency(dependencies);
    this.saveOhPackagePathInfoToLoader();
    const updatedInfo = configFileLoader.getConfigFileJson(nodeOhPackagePath);
    updatedInfo.dependencies = dependencies;
    configFileLoader.setConfigFileJson(nodeOhPackagePath, updatedInfo);
    this.isUpdatedOhPackageInfo = true;
  }

  public setDevDependenciesOpt(nodeOhPackagePath: string, devDependencies: any) {
    this.validateDependency(devDependencies);
    this.saveOhPackagePathInfoToLoader();
    const updatedInfo = configFileLoader.getConfigFileJson(nodeOhPackagePath);
    updatedInfo.devDependencies = devDependencies;
    configFileLoader.setConfigFileJson(nodeOhPackagePath, updatedInfo);
    this.isUpdatedOhPackageInfo = true;
  }

  public setDynamicDependenciesOpt(nodeOhPackagePath: string, dynamicDependencies: any) {
    this.validateDependency(dynamicDependencies);
    this.saveOhPackagePathInfoToLoader();
    const updatedInfo = configFileLoader.getConfigFileJson(nodeOhPackagePath);
    updatedInfo.dynamicDependencies = dynamicDependencies;
    configFileLoader.setConfigFileJson(nodeOhPackagePath, updatedInfo);
    this.isUpdatedOhPackageInfo = true;
  }

  public getVersion(nodeOhPackagePath: string) {
    this.saveOhPackagePathInfoToLoader();
    return configFileLoader.getConfigFileJson(nodeOhPackagePath).version;
  }

  public setVersion(nodeOhPackagePath: string, updateVersion: string) {
    // hvigorfile.ts调用此方法时已是运行态，此时未做类型校验
    if (typeof updateVersion !== 'string') {
      _log.printErrorExit('VERSION_IS_NOT_A_STRING', [updateVersion]);
    }
    this.saveOhPackagePathInfoToLoader();
    const updatedInfo = configFileLoader.getConfigFileJson(nodeOhPackagePath);
    updatedInfo.version = updateVersion;
    configFileLoader.setConfigFileJson(nodeOhPackagePath, updatedInfo);
    this.isUpdatedOhPackageInfo = true;
  }

  /**
   * 判断是否需要执行ohpm install
   * 1.是否更新oh-package.json5文件
   * 2.是否更新parameterFile文件
   * 3.是否使用includeNode及excludeNodeByName等api动态修改配置
   */
  public shouldDoOhpmInstall() {
    return this.isUpdatedOhPackageInfo || this.isChangedParameterFile || nodeConfigPathInfo.getIsModuleChanged() || hvigor.getParameter().getExtParam(CommonConst.ENFORCE_OHPM) === 'true';
  }

  public loadUpdatedOhPackageToDisk(targetName: string, newParameterFile?: string) {
    _log.debug('start to load updatedOhPackageInfo to the disk');

    // 如果没有写自定义任务，也要生成对应的依赖文件
    this.saveOhPackagePathInfoToLoader();

    // 落盘文件路径
    const ohPackageDirPath = path.resolve(PathUtil.getHvigorCacheDir(), CommonConst.DEPENDENCYMAP, targetName);
    const dependencyMapPath = path.resolve(ohPackageDirPath, `${CommonConst.DEPENDENCYMAP}.json5`);
    // 删除dependencyMap下的文件 防止目录结构不同导致ohpm下载失败
    fse.removeSync(path.resolve(PathUtil.getHvigorCacheDir(), CommonConst.DEPENDENCYMAP));
    fse.mkdirSync(ohPackageDirPath, { recursive: true });

    // dependencyMap对象
    const dependencyMap: DependencyMap = {
      basePath: dependencyMapPath,
      targetName: targetName || undefined,
      rootDependency: `./${CommonConst.OH_PACKAGE_JSON5}`,
      dependencyMap: {},
      modules: [],
    };


    hvigor.getAllNodes().forEach((node) => {
      // 当前node下的oh-Package-json5的路径，用于找到configFileLoader里所存储的对象值
      const nodeOhPackagePath = path.resolve(node.getNodePath(), CommonConst.OH_PACKAGE_JSON5);
      // 工程级oh-package.json5文件直接在根目录下生成
      if (node.getNodeName() === hvigor.getRootNode().getNodeName() && node.classKind === ClassTypeConst.HVIGOR_PROJECT) {
        // 写入工程级别oh-package.json文件
        const nodeOhPackageobj = configFileLoader.getConfigFileJson(nodeOhPackagePath);
        nodeOhPackageobj.parameterFile = newParameterFile;
        fse.writeFileSync(path.resolve(ohPackageDirPath, CommonConst.OH_PACKAGE_JSON5), JSON.stringify(nodeOhPackageobj));
        this.OhPackagePathMap.set(nodeOhPackagePath, path.resolve(ohPackageDirPath, CommonConst.OH_PACKAGE_JSON5));
        this.UpdatePathToOriginMap.set(path.resolve(ohPackageDirPath, CommonConst.OH_PACKAGE_JSON5), nodeOhPackagePath);
        return;
      }

      dependencyMap.modules.push({
        name: `${node.getNodeName()}`,
        srcPath: `${path.relative(dependencyMapPath, node.getNodePath())}`,
      });

      // 模块级别创建文件夹并生成oh-package.json5文件
      if (!fse.existsSync(path.resolve(ohPackageDirPath, node.getNodeName()))) {
        fse.mkdirSync(path.resolve(ohPackageDirPath, node.getNodeName()));
      }
      fse.writeFileSync(
        path.resolve(ohPackageDirPath, node.getNodeName(), CommonConst.OH_PACKAGE_JSON5),
        JSON.stringify(configFileLoader.getConfigFileJson(nodeOhPackagePath)),
      );

      // 存入新旧路径转换Map
      this.OhPackagePathMap.set(nodeOhPackagePath, path.resolve(ohPackageDirPath, node.getNodeName(), CommonConst.OH_PACKAGE_JSON5));
      this.UpdatePathToOriginMap.set(path.resolve(ohPackageDirPath, node.getNodeName(), CommonConst.OH_PACKAGE_JSON5), nodeOhPackagePath);

      // 将targetName以及路径赋值给dependencyMap
      dependencyMap.dependencyMap[node.getNodeName()] = `./${node.getNodeName()}/${CommonConst.OH_PACKAGE_JSON5}`;
    });

    // 将dependencyMap写入到目录下
    fse.writeFileSync(dependencyMapPath, JSON.stringify(dependencyMap));
    _log.debug('load to the disk finished');
  }

  // 如果修改了ohPackage.json 此时代码里的硬编码读取ohPackagePath都需求更改
  public getNodeOhPackagePath(path: string): string {
    if (!this.isUpdatedOhPackageInfo) {
      return path;
    }
    return this.OhPackagePathMap?.get(path) || path;
  }

  private saveOhPackagePathInfoToLoader(): void {
    if (this.isLoaded) {
      return;
    }

    // 将所有oh-package信息存入loader，通过hvigor对象获取所有Node的oh-package-json5路径
    hvigor.getAllNodes().forEach((node) => {
      const nodeOhPackagePath = path.resolve(node.getNodePath(), CommonConst.OH_PACKAGE_JSON5);
      configFileLoader.setConfigFileJson(nodeOhPackagePath, getJson5Obj(nodeOhPackagePath));
    });
    this.isLoaded = true;
  }

  private validateDependency(dependency: any) {
    for (const dependencyName in dependency) {
      if (typeof dependency[dependencyName] !== 'string') {
        _log.printErrorExit('DEPENDENCY_VERSION_IS_NOT_A_STRING', [dependency[dependencyName]]);
      }
    }
  }

  public getOriginPathFromUpdatePath(paths: string[]) {
    if (!this.isUpdatedOhPackageInfo) {
      return (paths = paths.map((ohPackagePath) => path.dirname(ohPackagePath)));
    }
    paths.forEach((ohDepPath, index) => {
      ohDepPath = ohDepPath.includes(CommonConst.OH_PACKAGE_JSON5) ? ohDepPath : path.resolve(ohDepPath, CommonConst.OH_PACKAGE_JSON5);
      const originDependencyPath = ohPackageLoader.UpdatePathToOriginMap.get(ohDepPath);
      paths[index] = originDependencyPath ? path.dirname(originDependencyPath) : path.dirname(ohDepPath);
    });
    return paths;
  }

  public setIsChangeParameterFile(targetName: string, newParameterFile?: string): void {
    if (typeof targetName !== 'string') {
      _log.printErrorExit('OHOS_ALIGN_TARGET_TYPE_ERROR', [path.resolve(hvigor.getRootNode().getNodePath(), 'hvigor', 'hvigor-config.json5')]);
      return;
    }
    const oldOhPackageJsonPath = path.resolve(PathUtil.getHvigorCacheDir(), CommonConst.DEPENDENCYMAP, targetName, CommonConst.OH_PACKAGE_JSON5);
    if (!fse.existsSync(oldOhPackageJsonPath) && newParameterFile) {
      return;
    }
    const oldOhPackageJsonObj = getJson5Obj(oldOhPackageJsonPath);
    const oldParameterFile = oldOhPackageJsonObj?.parameterFile;
    this.isChangedParameterFile = oldParameterFile !== newParameterFile;
  }

  /**
   * 获取overrides
   * @param nodeOhPackagePath
   */
  public getOverrides(nodeOhPackagePath: string): any {
    this.saveOhPackagePathInfoToLoader();
    return this.getOhPackageJsonObj(nodeOhPackagePath)?.overrides;
  }

  /**
   * 设置overrides
   * @param nodeOhPackagePath
   * @param overrides
   */
  public setOverrides(nodeOhPackagePath: string, overrides: any) {
    this.validateDependency(overrides);
    this.saveOhPackagePathInfoToLoader();
    const updatedInfo = configFileLoader.getConfigFileJson(nodeOhPackagePath);
    updatedInfo.overrides = overrides;
    configFileLoader.setConfigFileJson(nodeOhPackagePath, updatedInfo);
    this.isUpdatedOhPackageInfo = true;
  }
}

export interface DependencyMap {
  targetName: string | undefined;
  rootDependency: string;
  dependencyMap: Record<string, string>;
  modules: HvigorModule[];
  // dependencyMap.json5文件绝对路径
  basePath: string;
}

type HvigorModule = {
  name: string;
  // 模块节点相对于dependencyMap.json5文件的相对路径
  srcPath: string;
};

export const ohPackageLoader = new OhPackageLoader();
